home *** CD-ROM | disk | FTP | other *** search
- /*
- * General skeleton for adding options to the access control language. The
- * Makefile describes how this alternative language is enabled. Shell
- * commands will still be available, be it with a slightly different syntax.
- *
- * The code uses a slightly different format of access control rules. It
- * assumes that an access control rule looks like this:
- *
- * daemon_list : client_list : option : option ...
- *
- * An option is of the form "keyword" or "keyword = value". Option fields are
- * processed from left to right. Blanks around keywords, "=" and values are
- * optional. Blanks within values are left alone.
- *
- * Diagnostics are reported through syslog(3).
- *
- * Examples of options that are already implemented by the current skeleton:
- *
- * user = nobody
- *
- * Causes the process to switch its user id to that of "nobody". This normally
- * requires root privilege.
- *
- * group = tty
- *
- * Causes the process to change its group id to that of the "tty" group. In
- * order to switch both user and group ids you should normally switch the
- * group id before switching the user id.
- *
- * setenv = name value
- *
- * places a name,value pair into the environment. The value is subjected to
- * %<character> expansions.
- *
- * spawn = (/usr/ucb/finger -l @%h | /usr/ucb/mail root) &
- *
- * Executes (in a background child process) the shell command "finger -l @%h |
- * mail root" after doing the %<character> expansions described in the
- * hosts_access(5) manual page. The command is executed with stdin, stdout
- * and stderr connected to the null device. Because options are processed in
- * order, multiple spawn comands can be specified within the same access
- * control rule, though "spawn = command1; command2" would be more
- * efficient.
- *
- * in.ftpd : ... : twist = /bin/echo 421 Some customized bounce message
- *
- * Sends some custmized bounce message to the remote client instead of running
- * the real ftp daemon. The command is subjected to %<character> expansion
- * before execution by /bin/sh. Stdin, stdout and stderr are connected to the
- * remote client process. The twist'ed command overlays the current process;
- * it makes no sense to specify other options on the same line after a
- * "twist". The "twist" option was inspired by Dan Bernstein's shuctl daemon
- * wrapper control language.
- *
- * umask = value
- *
- * Sets the process file creation mask. Value must be an octal number.
- *
- * If you compile with -DRFC_OPTION, code is enabled for the following option
- * that does selective rfc931 lookups.
- *
- * rfc931
- *
- * Causes the daemon front ends to look up the remote user name with the RFC
- * 931 protocol.
- *
- * Warnings:
- *
- * This module uses the non-reentrant strtok() library routine. The options
- * argument to process_options() is destroyed.
- *
- * There cannot be a ":" character in keywords or values. Backslash sequences
- * are not yet recognized.
- *
- * In case of UDP connections, do not "twist" commands that use the standard
- * I/O or read(2)/write(2) routines to communicate with the client process;
- * UDP requires other communications primitives.
- *
- * In case of errors, use clean_exit() instead of directly calling exit(), or
- * your inetd may loop on an UDP request.
- */
-
- /* System libraries. */
-
- #include <sys/types.h>
- #include <sys/param.h>
- #include <sys/socket.h>
- #include <sys/stat.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <stdio.h>
- #include <syslog.h>
- #include <pwd.h>
- #include <grp.h>
- #include <ctype.h>
-
- extern char *strtok();
- extern char *strchr();
- extern void closelog();
-
- /* Local stuff. */
-
- #include "log_tcp.h"
-
- /* List of functions that implement the options. Add yours here. */
-
- static void user_option(); /* execute "user=name" option */
- static void group_option(); /* execute "group=name" option */
- static void umask_option(); /* execute "umask=mask" option */
- static void twist_option(); /* execute "twist=command" option */
- #ifdef RFC931_OPTION
- static void rfc931_option(); /* execute "rfc931" option */
- #endif
- static void setenv_option(); /* execute "setenv=name value" */
-
- static char *chop_string(); /* strip leading and trailing blanks */
-
- /* Structure of the options table. */
-
- struct option {
- char *name; /* keyword name, case does not matter */
- int need_value; /* value required or not */
- void (*func) (); /* function that does the real work */
- };
-
- /* List of known keywords. Add yours here. */
-
- static struct option option_table[] = {
- "user", 1, user_option, /* switch user id */
- "group", 1, group_option, /* switch group id */
- "umask", 1, umask_option, /* change umask */
- "spawn", 1, shell_cmd, /* spawn shell command */
- "twist", 1, twist_option, /* replace current process */
- #ifdef RFC931_OPTION
- "rfc931", 0, rfc931_option, /* do RFC 931 lookup */
- #endif
- "setenv", 1, setenv_option, /* update environment */
- 0,
- };
-
- static char whitespace[] = " \t\r\n";
-
- /* process_options - process optional access control information */
-
- process_options(options, daemon, client)
- char *options;
- char *daemon;
- struct from_host *client;
- {
- char *key;
- char *value;
- struct option *op;
-
- /*
- * Light-weight parser. Remember, we may be running as root so we need
- * code that is easy to comprehend.
- */
-
- for (key = strtok(options, ":"); key; key = strtok((char *) 0, ":")) {
- if (value = strchr(key, '=')) { /* keyword=value */
- *value++ = 0;
- value = chop_string(value); /* strip blanks around value */
- if (*value == 0)
- value = 0; /* no value left */
- }
- key = chop_string(key); /* strip blanks around key */
- for (op = option_table; op->name; op++) /* find keyword */
- if (strcasecmp(op->name, key) == 0)
- break;
- if (op->name == 0) {
- syslog(LOG_ERR, "bad option or syntax: \"%s\"", key);
- } else if (value == 0 && op->need_value) {
- syslog(LOG_ERR, "option \"%s\" requires value", key);
- } else if (value && op->need_value == 0) {
- syslog(LOG_ERR, "option \"%s\" requires no value", key);
- } else {
- (*(op->func)) (value, daemon, client);
- }
- }
- }
-
- /* user_option - switch user id */
-
- /* ARGSUSED */
-
- static void user_option(value, daemon, client)
- char *value;
- char *daemon;
- struct from_host *client;
- {
- struct passwd *pwd;
- struct passwd *getpwnam();
-
- if ((pwd = getpwnam(value)) == 0) {
- syslog(LOG_ERR, "unknown user: \"%s\"", value);
- clean_exit(client);
- } else if (setuid(pwd->pw_uid)) {
- syslog(LOG_ERR, "setuid(%s): %m", value);
- clean_exit(client);
- }
- }
-
- /* group_option - switch group id */
-
- /* ARGSUSED */
-
- static void group_option(value, daemon, client)
- char *value;
- char *daemon;
- struct from_host *client;
- {
- struct group *grp;
- struct group *getgrnam();
-
- if ((grp = getgrnam(value)) == 0) {
- syslog(LOG_ERR, "unknown group: \"%s\"", value);
- clean_exit(client);
- } else if (setgid(grp->gr_gid)) {
- syslog(LOG_ERR, "setgid(%s): %m", value);
- clean_exit(client);
- }
- }
-
- /* umask_option - set file creation mask */
-
- /* ARGSUSED */
-
- static void umask_option(value, daemon, client)
- char *value;
- char *daemon;
- struct from_host *client;
- {
- unsigned mask;
- char junk;
-
- if (sscanf(value, "%o%c", &mask, &junk) != 1 || (mask & 0777) != mask) {
- syslog(LOG_ERR, "bad umask: \"%s\"", value);
- clean_exit(client);
- }
- (void) umask(mask);
- }
-
- /* twist_option - replace process by shell command */
-
- static void twist_option(value, daemon, client)
- char *value;
- char *daemon;
- struct from_host *client;
- {
- char buf[BUFSIZ];
- int pid = getpid();
- char *error;
-
- percent_x(buf, sizeof(buf), value, daemon, client, pid);
-
- /* Since we will not be logging in the usual way, do it here and now. */
-
- syslog(SEVERITY, "twist from %s to %s", hosts_info(client), buf);
- closelog();
-
- /*
- * Before switching to the shell, set up stdout and stderr in case the
- * Ultrix inetd didn't.
- */
-
- (void) close(1);
- (void) close(2);
- if (dup(0) != 1 || dup(0) != 2) {
- error = "dup: %m";
- } else {
- (void) execl("/bin/sh", "sh", "-c", buf, (char *) 0);
- error = "/bin/sh: %m";
- }
-
- /* Can get here only in case of errors. */
-
- #ifdef LOG_MAIL
- (void) openlog(daemon, LOG_PID, FACILITY);
- #else
- (void) openlog(daemon, LOG_PID);
- #endif
- syslog(LOG_ERR, error);
- clean_exit(client);
- }
-
- #ifdef RFC931_OPTION
-
- /* rfc931_option - look up remote user name */
-
- /* ARGSUSED */
-
- static void rfc931_option(value, daemon, client)
- char *value;
- char *daemon;
- struct from_host *client;
- {
- if (client->sock_type == FROM_CONNECTED) {
- if (client->sin == 0) {
- syslog(LOG_ERR, "no socket info for username lookup");
- } else {
- client->user = rfc931_name(client->sin);
- }
- }
- }
-
- #endif
-
- /* setenv_option - set environment variable */
-
- /* ARGSUSED */
-
- static void setenv_option(value, daemon, client)
- char *value;
- char *daemon;
- struct from_host *client;
- {
- char *var_name;
- char *var_value;
- char buf[BUFSIZ];
- int pid;
-
- /*
- * What we get is one string with the name and the value separated by
- * whitespace. Find the end of the name. If that is also the end of the
- * string, the value is empty.
- */
-
- var_value = value + strcspn(value, whitespace);
-
- if (*var_value == 0) { /* just a name, that's all */
- var_name = value;
- } else { /* expand %stuff in value */
- *var_value++ = 0;
- var_name = chop_string(value);
- pid = getpid();
- percent_x(buf, sizeof(buf), var_value, daemon, client, pid);
- var_value = chop_string(buf);
- }
- if (setenv(var_name, var_value, 1)) {
- syslog(LOG_ERR, "memory allocation failure");
- clean_exit(client);
- }
- }
-
- /* chop_string - strip leading and trailing blanks from string */
-
- static char *chop_string(start)
- register char *start;
- {
- register char *end;
-
- while (*start && isspace(*start))
- start++;
-
- for (end = start + strlen(start); end > start && isspace(end[-1]); end--)
- /* void */ ;
- *end = 0;
-
- return (start);
- }
-